﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Mail;

namespace Lr.Core.Web.Mailing
{
    /// <summary>
    /// Base implementation of <see cref="IMailer"/>.
    /// </summary>
    public class Mailer : IMailer
    {
        private readonly SmtpClient _smtpClient;
        private readonly MailAddress _sender;

        /// <summary>
        /// Constructs a new <see cref="Mailer"/> instance.
        /// </summary>
        /// <param name="smtpClient">The <see cref="SmtpClient"/> to use as mail engine.</param>
        /// <param name="sender">The <see cref="MailAddress"/> to use as sender.</param>
        public Mailer(SmtpClient smtpClient, MailAddress sender)
        {
            ThrowIf.Null(() => smtpClient);
            ThrowIf.Null(() => sender);

            ThrowIf.NullOrEmpty(() => sender.Host);
            ThrowIf.NullOrEmpty(() => sender.Address);
            ThrowIf.NullOrEmpty(() => sender.DisplayName);

            _smtpClient = smtpClient;
            _sender = sender;
        }

        /// <summary>
        /// Sends out an e-mail.
        /// </summary>
        /// <param name="body">The body of the e-mail.</param>
        /// <param name="subject">The subject of the e-mail.</param>
        /// <param name="to">The e-mail address of the reciever.</param>
        /// <param name="cc">The e-mail adresses of the people in cc.</param>
        /// <param name="bcc">The e-mail addresses of the people in bcc.</param>
        /// <returns>Null when mail was sent ok, an <see cref="Exception"/> when something went wrong.</returns>
        public Exception Send(string body, string subject, string to, IEnumerable<string> cc = null, IEnumerable<string> bcc = null)
        {
            return Send(body, subject, new string[] { to }, cc, bcc);
        }

        /// <summary>
        /// Sends out an e-mail.
        /// </summary>
        /// <param name="body">The body of the e-mail.</param>
        /// <param name="subject">The subject of the e-mail.</param>
        /// <param name="to">The e-mail addresses of the recievers.</param>
        /// <param name="cc">The e-mail adresses of the people in cc.</param>
        /// <param name="bcc">The e-mail addresses of the people in bcc.</param>
        /// <returns>Null when mail was sent ok, an <see cref="Exception"/> when something went wrong.</returns>
        public Exception Send(string body, string subject, IEnumerable<string> to, IEnumerable<string> cc = null, IEnumerable<string> bcc = null)
        {
            try {
                var message = GetBaseMessage();

                message.Body = body;
                message.Subject = subject;
                message.To.Add(FlattenEmailAddresses(to));
                if (cc != null)
                    if (cc.Count() != 0)
                        message.CC.Add(FlattenEmailAddresses(cc));
                if (bcc != null)
                    if (bcc.Count() != 0)
                        message.Bcc.Add(FlattenEmailAddresses(bcc));

                _smtpClient.Send(message);

                return null;
            } catch (Exception ex) {
                return ex;
            }
        }

        /// <summary>
        /// Flattens an <see cref="IEnumerable"/> of <see cref="string"/> to a single string with seperated a comma and a space.
        /// </summary>
        /// <param name="emailAdresses">The <see cref="IEnumerable"/> of <see cref="string"/>s that contain e-mail addresses.</param>
        /// <returns>A single string with seperated a comma and a space</returns>
        private string FlattenEmailAddresses(IEnumerable<string> emailAdresses)
        {
            var flattened = emailAdresses
                .Flatten(", ", "", "")
                .RemoveMultipleWhitespaces();

            return flattened;
        }

        /// <summary>
        /// Gets the base <see cref="MailMessage"/>.
        /// </summary>
        /// <returns>A base <see cref="MailMessage"/>.</returns>
        protected virtual MailMessage GetBaseMessage()
        {
            var baseMessage = new MailMessage();

            baseMessage.IsBodyHtml = false;
            baseMessage.Sender = _sender;
            baseMessage.From = _sender;

            return baseMessage;
        }

        /// <summary>
        /// Performs a SmtpClient.Dispose() call.
        /// </summary>
        public void Dispose()
        {
            if (_smtpClient != null)
                _smtpClient.Dispose();
        }
    }
}
